package com.zh.blog.strategy.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zh.blog.dao.UserAuthDao;
import com.zh.blog.dao.UserInfoDao;
import com.zh.blog.dao.UserRoleDao;
import com.zh.blog.dto.SocialTokenDto;
import com.zh.blog.dto.SocialUserInfoDto;
import com.zh.blog.dto.UserDetailDto;
import com.zh.blog.dto.UserInfoDto;
import com.zh.blog.enums.RoleEnum;
import com.zh.blog.exception.BizException;
import com.zh.blog.model.UserAuth;
import com.zh.blog.model.UserInfo;
import com.zh.blog.model.UserRole;
import com.zh.blog.service.impl.UserDetailsServiceImpl;
import com.zh.blog.strategy.SocialLoginStrategy;
import com.zh.blog.util.BeanCopyUtils;
import com.zh.blog.util.IpUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Objects;

import static com.zh.blog.constant.CommonConst.TRUE;
import static com.zh.blog.enums.ZoneEnum.SHANGHAI;

/**
 * 第三方登录抽象模板
 * @author ZhengHao
 * Created At 2022/8/25.
 **/
@Service
public abstract class AbstractSocialLoginStrategyImpl implements SocialLoginStrategy {
    @Autowired
    private UserAuthDao userAuthDao;
    @Autowired
    private UserInfoDao userInfoDao;
    @Autowired
    private UserRoleDao userRoleDao;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Resource
    private HttpServletRequest request;
    @Override
    public UserInfoDto login(String data) {
        // 创建登录信息
        UserDetailDto userDetailDto;
        // 获取第三方token信息
        SocialTokenDto socialToken = getSocialToken(data);
        // 获取用户IP信息
        String ipAddress = IpUtils.getIpAddress(request);
        String ipSource = IpUtils.getIpSource(ipAddress);
        // 判断是否已经注册过
        UserAuth user = getUserAuth(socialToken);
        if (Objects.nonNull(user)) {
            // 返回数据库用户信息
            userDetailDto = getUserDetail(user, ipAddress, ipSource);
        } else {
            // 获取第三方用户信息，保存到数据库返回
            userDetailDto = saveUserDetail(socialToken, ipAddress, ipSource);
        }
        // 判断账号是否禁用
        if (userDetailDto.getIsDisable().equals(TRUE)) {
            throw new BizException("账号已经被禁用");
        }
        // 将登录信息放入SpringSecurity管理
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetailDto, null, userDetailDto.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(auth);
        // 返回用户信息
        return BeanCopyUtils.copyObject(userDetailDto, UserInfoDto.class);
    }

    /**
     * 新增用户信息
     * @param socialToken token信息
     * @param ipAddress IP地址
     * @param ipSource IP源
     * @return 用户信息
     */
    private UserDetailDto saveUserDetail(SocialTokenDto socialToken, String ipAddress, String ipSource) {
        // 获取第三方用户信息
        SocialUserInfoDto socialUserInfo = getSocialUserInfo(socialToken);
        // 保存用户信息
        UserInfo userInfo = UserInfo.builder()
                .nickname(socialUserInfo.getNickname())
                .avatar(socialUserInfo.getAvatar())
                .build();
        userInfoDao.insert(userInfo);
        // 保存账号信息
        UserAuth userAuth = UserAuth.builder()
                .userInfoId(userInfo.getId())
                .username(socialToken.getOpenId())
                .password(socialToken.getAccessToken())
                .loginType(socialToken.getLoginType())
                .lastLoginTime(LocalDateTime.now(ZoneId.of(SHANGHAI.getZone())))
                .ipAddress(ipAddress)
                .ipSource(ipSource)
                .build();
        userAuthDao.insert(userAuth);
        // 绑定角色
        UserRole userRole = UserRole.builder()
                .userId(userInfo.getId())
                .roleId(RoleEnum.USER.getRoleId())
                .build();
        userRoleDao.insert(userRole);
        return userDetailsService.convertUserDetail(userAuth, request);
    }

    /**
     * 获取用户信息
     * @param user 用户账号信息
     * @param ipAddress IP地址
     * @param ipSource IP源
     * @return 用户信息
     */
    private UserDetailDto getUserDetail(UserAuth user, String ipAddress, String ipSource) {
        // 更新登录账号
        userAuthDao.update(new UserAuth(), new LambdaUpdateWrapper<UserAuth>()
            .set(UserAuth::getLastLoginTime, LocalDateTime.now())
            .set(UserAuth::getIpAddress, ipAddress)
            .set(UserAuth::getIpSource, ipSource)
            .eq(UserAuth::getId, user.getId()));
        // 封装信息
        return userDetailsService.convertUserDetail(user, request);
    }

    /**
     * 获取用户账号
     * @param socialToken 信息
     * @return 用户账号
     */
    private UserAuth getUserAuth(SocialTokenDto socialToken) {
        return userAuthDao.selectOne(new LambdaQueryWrapper<UserAuth>()
            .eq(UserAuth::getUsername, socialToken.getOpenId())
            .eq(UserAuth::getLoginType,socialToken.getLoginType()));
    }

    /**
     * 获取第三方用户信息
     * @param socialToken 第三方token信息
     * @return 第三方用户信息
     */
    public abstract SocialUserInfoDto getSocialUserInfo(SocialTokenDto socialToken);

    /**
     * 获取第三方token信息
     * @param data 数据
     * @return 第三方token信息
     */
    public abstract SocialTokenDto getSocialToken(String data);
}